package game;

import game.connection.GameClient;
import game.connection.GameEvent;
import game.connection.GameInitiate;
import game.event.StatusHandler;
import game.snake.SnakeBody;
import game.snake.direction.Direction;
import game.snake.direction.DirectionDown;
import game.snake.direction.DirectionLeft;
import game.snake.direction.DirectionRight;
import game.snake.direction.DirectionUp;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.IOException;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.Timer;

/**
 * This class manage the game on client side.
 * @author Sarun Wongtanakarn 5510546166
 * @author Apiwat Jierakulpanit 5510546212
 * @Version 2013.05.01
 */
public class GameController implements Observer {

	private GameInitiate game;
	private GameClient client;
	private Direction direction;
	private boolean isMove;
	private Timer timer;
	private GameDraw gameDraw;
	private int lastKey;
	private Canvas canvas;

	/**
	 * Creates the GmaeController with giving GameInitiate and Client.
	 * @param comingGame the game of client
	 * @param gameClient the client
	 */
	public GameController(GameInitiate comingGame, GameClient gameClient) {

		game = comingGame;
		this.client = gameClient;

		lastKey = KeyEvent.VK_RIGHT;
	
		canvas = new Canvas(this, game.FIELD.getWidth() * 19,
				game.FIELD.getHeight() * 11);
		gameDraw = new GameDraw(game);
		canvas.add(gameDraw);
		
		timer = new Timer(game.INTERVAL, new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				getKey(lastKey);
				sendEventToServer();
			}
		});
		
		timer.start();
	}

	@Override
	public void update(Observable arg0, Object arg1) {
		GameEvent event = (GameEvent) arg1;
		if (isMove)
			game.PLAYER.getSnake().getDirection().changeDirection(direction);
		handleEvent(event);
		updateCanvas(event);
		isMove = false;
		direction = game.PLAYER.getSnake().getDirection();
	}

	/**
	 * Updates the Canvas with giving GameEvent.
	 * @param event the new event from the server
	 */
	private void updateCanvas(GameEvent event) {
		gameDraw.setField(game.FIELD);
		gameDraw.setHealth(event.getOpponentData());
		canvas.repaint(game.INTERVAL);

	}

	/**
	 * Receives the event and update the value on field.
	 * @param event the new event from server
	 */
	private void handleEvent(GameEvent event) {

		Map<Point, Integer> moveMap = event.getMoveMap();

		StatusHandler.handle(game.PLAYER, event.getStatusEvent());
		if (isMove)
			game.PLAYER.getSnake().move();

		for (Point p : moveMap.keySet())
			game.FIELD.setValue(p, moveMap.get(p));

		if (event.isGrowth())
			game.PLAYER.getSnake().growth(1, game.FIELD.POINT);

		canvas.repaint(game.INTERVAL);

	}

	/** Creates event and sends it to server. */
	private void sendEventToServer() {
		GameEvent evt = new GameEvent();

		if (game.PLAYER.getHealth() == 0) {
			timer.stop();
			game.PLAYER.setHealth(-1);
		}
		
		SnakeBody currentHead = game.PLAYER.getSnake().getHead();
		int x = currentHead.getPoint().X;
		int y = currentHead.getPoint().Y;

		if (direction.getClass() == DirectionDown.class) {
			y--;
			if (y < 0)
				y = game.FIELD.getHeight() - 1;
		} else if (direction.getClass() == DirectionUp.class) {
			y++;
			if (y > game.FIELD.getHeight() - 1)
				y = 0;

		} else if (direction.getClass() == DirectionLeft.class) {
			x--;
			if (x < 0)
				x = game.FIELD.getWidth() - 1;

		} else if (direction.getClass() == DirectionRight.class) {
			x++;
			if (x > game.FIELD.getWidth() - 1) 
				x = 0;
		}

		try {
			evt.addMoveEvent(game.FIELD.POINT[x][y], game.PLAYER.ID);
			evt.addMoveEvent(game.PLAYER.getSnake().getTail().getPoint(), 0);
			evt.addOpponentData(game.PLAYER.ID, game.PLAYER.getHealth());
			isMove = true;
		} catch (Exception ex) { }

		try {
			client.sendToServer(evt);
		} catch (IOException ex) {
			ex.printStackTrace();
		}

	}

	/** 
	 * Receives the key code and changes the direction of snake.
	 * @param keyCode the code of key
	 */
	public void getKey(int keyCode) {
		if (keyCode == KeyEvent.VK_LEFT)
			checkKey(game.PLAYER.getSnake().RIGHT, game.PLAYER.getSnake().LEFT);
		else if (keyCode == KeyEvent.VK_UP)
			checkKey(game.PLAYER.getSnake().DOWN, game.PLAYER.getSnake().UP);
		else if (keyCode == KeyEvent.VK_RIGHT)
			checkKey(game.PLAYER.getSnake().LEFT, game.PLAYER.getSnake().RIGHT);
		else if (keyCode == KeyEvent.VK_DOWN)
			checkKey(game.PLAYER.getSnake().UP, game.PLAYER.getSnake().DOWN);

		lastKey = keyCode;

	}

	/**
	 * Checks the direction that is not the opposite direction of the current direction.
	 * @param oppositeDirection the opposite direction
	 * @param newDirection the direction for setting
	 */
	private void checkKey(Direction oppositeDirection, Direction newDirection) {
		if (game.PLAYER.getSnake().getDirection() != oppositeDirection)
			direction = newDirection;
	}

}
